package com.domain;

import java.util.Date;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.dequeue.DeQueueList;
import com.queue.PriorityQueueList;
import com.stack.StackList;

/**Esta clase es la encargada de Administrar las llegada, salidas y retiradas del
 aeropuerto, ademas de que al momento de instanciarla, crea un escenario aleatorio
 de aeropuerto.*/
public class Aeropuerto {
	
	private Random r;
        private long semilla;
        private static Logger LOG =  Logger.getLogger(Aeropuerto.class .getName());;
        private StackList auxStack;
	private DeQueueList taxiLane;
	private PriorityQueueList holdingPattern;
	
	public final boolean TAXI_LANE_IS_FULL = true;
        static final int CAPACIDAD_ESTACIONAMIENTO = 16;
        
        /**
         * Este es el contructos predeterminado, el cual inicializa las variables
         * y de generar un escenario aleatorio.
         */
	public Aeropuerto(){		
		auxStack = new StackList();
		taxiLane = new DeQueueList();
		holdingPattern = new PriorityQueueList();
		
		r = new Random();
		semilla = new Date().getTime();
                generacionAleatoria();
		 
		LOG.setLevel(Level.INFO);
	}
        
        /**Agrega un nuevo avion a el estacionamiento. En caso de que el esta
         * cionamiento este lleno, pone el avion en la cola de espera.
         * @param Avion El avion a introducir
         * @return Boolean si la operacion se realizo con exito o no.
         */
	public boolean llegada(Avion nuevo) {
		if (taxiLane.size() == CAPACIDAD_ESTACIONAMIENTO) {
			holdingPattern.insertItem(nuevo, nuevo.getCantidadFuel());
			return true;
		} else {
			taxiLane.insertFirst(nuevo);
			
			LOG.info(nuevo.toDataString());
			return false;
		}
	}
        
        /**
         * Metodo que retira el ultimo avion del estacionamiento (el primero que
         * entro) y en caso de que la cola de prioridad no este vacia, agrega al 
         * estacionamiento el elemento mas prioritario. Arroja un excepcion si 
         * el estacionamiento esta vacio.
         * @return Avion Objeto retirado del estacionamiento.
         * @throws EmptyParkException Si el estacionamiento esta vacio.
         */
	public Avion salida() throws EmptyParkException {
		if (taxiLane.isEmpty())
			throw new EmptyParkException();		

		Avion avionSalida = (Avion) taxiLane.removeFromLast();

		if (!holdingPattern.isEmpty()) {
			taxiLane.insertFirst(holdingPattern.removeMinElement());
		}
		LOG.info(avionSalida.toDataString());
		return avionSalida;
	}
        
        /**
         * Metodo que retira un avion descompuesto del estacionamiento. Saca los
         * elemento por el final y los mete en una pila de apoyo, hasta llegar al
         * elemento que se desea retirar. Arroja una excepcion si el estaciona-
         * miento esta vacio.
         * @param String Matricula del Avion a retirar.
         * @return Avion Avion eliminado.
         * @throws EmptyParkexception Si el estacionamiento esta vacio.        
         */
	public Avion retirar(String matricula) throws EmptyParkException {
		if (taxiLane.isEmpty())
			throw new EmptyParkException();
		
		while (!taxiLane.isEmpty()
				&& !((Avion) taxiLane.last()).getNumMatricula().equals(
						matricula)) {
			auxStack.push(taxiLane.removeFromLast());
		}

		Avion objetoRemovido = null;
		if (!taxiLane.isEmpty()){
			objetoRemovido = (Avion) taxiLane.removeFromLast();
			
		}
		while (!auxStack.isEmpty()) {
			taxiLane.insertLast(auxStack.pop());
		}
		
		return objetoRemovido;
	}	

	/**
	 * Metodos de Generación aleatoria en cada llamada al metodo cambia la
	 * semilla.
	 */
        
        /**
         * Metodo que genera un numero aleatorio con un rango maximo dado por el
         * usuario. Utiliza una semilla distinta cada vez que es invocado.
         * @param int Valor maximo que puede tomar el numero random.
         * @return int Numero aleatorio generado.         
         */
	private int numeroAleatorio(int maximo) {
		r.setSeed(semilla++);
		int numero = r.nextInt(maximo + 1);
		return (numero == 0) ? numero += 1 : numero;
	}
        
        /**
         * Metodo que genera una cadena de caracteres alphanuméricos, con un lon-
         * gitud determinada por el usuario. Utiliza un semilla distinta cada vez
         * que es invocado.
         * @param int Longitud de la cadena
         * @return String Matricula aleatoria.
         */
	private String matriculaAleatoria(int longitud) {
		String matricula = "";
		r.setSeed(semilla++);
		int i = 0;

		while (i < longitud) {
			char c = (char) r.nextInt(255);
			if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')) {
				matricula += c;
				i++;
			}
		}
		return matricula;
	}
        
        /**
         * Metodo que gebera un escenario aleatorio para el aeropuerto. Utiliza 
         * los metodos numeroAleatorio y matriculaAleatoria. Utiliza una semilla
         * distinta cada vez que le metodo es ivocado. Este metodo es invocado en
         * el constructor.
         */
	private void generacionAleatoria() {
		r.setSeed(semilla++);
		int avionesLlegadas = r.nextInt(25 + 1);
		int avionesSalidas = r.nextInt(avionesLlegadas);

		for (int i = 0; i < avionesLlegadas; i++)
			llegada(new Avion(matriculaAleatoria(10),
					numeroAleatorio(1000), numeroAleatorio(1000)));

		for (int i = 0; i < avionesSalidas; i++)
			salida();
                
                int retirados = r.nextInt(taxiLane.size());
                for(int i=0;i < retirados;i++)
                    retirar(((Avion)taxiLane.last()).getNumMatricula());
                        
	}
        
        /**Metodo que regresa la cola de prioridad.
         * @return PriorityQueueList Cola de espera.
         */
	public PriorityQueueList getEnEspera() {
		return holdingPattern;
	}

        /**Metodo que regresa la lista del estaciomaiento.
         * @return DeQueueQueueList Estacionameinto.
         */
	public DeQueueList getEstacionamiento() {
		return taxiLane;
	}
        
        /**Metodo que regresa la pila auxiliar.
         * @return stackList La pila auxiliar.
         */
	public StackList getPila() {
		return auxStack;
	}

}
